/*
 * @Author: Wangjun
 * @Date: 2023-07-10 09:08:17
 * @LastEditTime: 2025-07-02 11:14:53
 * @LastEditors: wangjun haodreams@163.com
 * @Description:获取机器的硬件码
 * @FilePath: \libs\hostcode\hostcode.go
 * hnxr
 */
package hostcode

import (
	"net"
	"os"
	"runtime"
	"strings"
	"time"
)

var gHardCode = ""

// 获取默认的硬件码路径
func GetDefaultCodePath() string {
	path := "/etc/hardcode"
	if runtime.GOOS == "windows" {
		path = "hardcode"
	}
	return path
}

// 通用虚拟网卡检测
func isVirtualInterface(iface net.Interface) bool {
	name := strings.ToLower(iface.Name)

	// 跳过回环接口
	if iface.Flags&net.FlagLoopback != 0 {
		return true
	}

	// 常见虚拟网卡标识
	virtualKeywords := []string{
		"docker", "dummy", "veth", "br-", "virbr", // Docker 和虚拟化
		"tun", "tap",                     // VPN
		"vmware", "virtual", "vbox",      // 虚拟机
		"ppp", "ipsec",                   // 拨号/VPN
		"lo",                             // 回环
	}

	for _, kw := range virtualKeywords {
		if strings.Contains(name, kw) {
			return true
		}
	}

	return false
}


func GetMacs() []string {
	info, err := net.Interfaces()
	if err != nil {
		return nil
	}
	cs := make([]string, 0, len(info))
	for _, inf := range info {
		//排除虚拟网卡， 虚拟网卡会变strings.HasPrefix(inf.Name, "dummy")
		if  isVirtualInterface(inf){
			continue
		}
		s := inf.HardwareAddr.String()
		if s != "" {
			s = strings.ReplaceAll(s, ":", "")
			s = strings.ToUpper(s)
			cs = append(cs, s)
		}
	}
	return cs
}

// GetHardCode 获取硬件码
func GetHardCode(paths ...string) string {
	if gHardCode != "" {
		return gHardCode
	}
	path := "hardcode"
	if len(paths) > 0 {
		path = paths[0]
	}
	code := time.Now().Format("060102150304")
	getCode := func(code string) string {
		data, err := os.ReadFile(path)
		if err != nil {
			gHardCode = code
			WriteFile(path, []byte(code), 0666)
			return code
		}
		return string(data)
	}

	cs := GetMacs()
	if len(cs) == 0 {
		gHardCode = code
		return getCode(code)
	}

	data := getCode(code)

	for _, s := range cs {
		if s == data {
			return s
		}
	}
	WriteFile(path, []byte(cs[0]), 0666)
	gHardCode = cs[0]
	return cs[0]
}

func WriteFile(name string, data []byte, perm os.FileMode) error {
	f, err := os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)

	if err != nil {
		return err
	}
	_, err = f.Write(data)
	f.Sync()
	if err1 := f.Close(); err1 != nil && err == nil {
		err = err1
	}
	return err
}
